home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
ups
/
apcd-0.5
/
apcd-0
/
apcd.c
next >
Wrap
C/C++ Source or Header
|
1995-11-07
|
15KB
|
671 lines
/*
* apcd.c - Daemon for the APC Smart UPS
*
* Copyright (c) 1995 Pavel Korensky
* All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL PAVEL KORENSKY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF PAVEL
* KORENSKY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* PAVEL KORENSKY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND PAVEL KORENSKY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/*
* Version:
*
* $Id: apcd.c,v 1.7 1995/11/07 12:40:03 root Exp root $
*
*
* History:
*
* $Log: apcd.c,v $
* Revision 1.7 1995/11/07 12:40:03 root
* Version 0.5 Beta, uploaded to the sunsite
*
* Revision 1.6 1995/11/01 15:25:28 root
* Several adaptations for clien/server - NOT FUNCTIONAL
*
* Revision 1.5 1995/05/23 07:25:08 root
* First public ALPHA version
*
* Revision 1.4 1995/05/23 01:07:40 root
* Parameters are on the command line, instead of config.h file
*
* Revision 1.3 1995/05/23 00:25:43 root
* System shutdown with UPS switch off was added
*
* Revision 1.2 1995/05/21 21:10:56 root
* Some small fixes
*
* Revision 1.1 1995/05/21 20:15:13 root
* Initial revision
*
*
*
*
*
*/
#include "apcd.h"
#include "version.h"
static char *version="$Id: apcd.c,v 1.7 1995/11/07 12:40:03 root Exp root $";
UPSINFO myUPS;
FILE *valfile;
FILE *UPSlogfile;
int killme, battlow;
int slave = 0;
int port;
int socketfd,newsocketfd;
char *use_port;
char *master_name;
char *logfilename;
char *slaves[MAX_SLAVES];
int num_slaves = 0;
int power_timer = 10;
int log_timer = 30;
int log_counter = 0;
int alarmup,alarmdown,pending,alarmcount,wasmsg;
int gottimeout = 0;
int mastertimeout = 0;
int gotpowerok = 0;
int masterbatlow = 0;
struct termios oldtio, newtio;
void main(int argc, char *argv[])
{
char msg[100];
int i;
time_t cas;
time(&cas);
strftime(msg,100,"%b %d %X",localtime(&cas));
printf("%s apcd:\n",msg);
use_port=calloc(100,sizeof(char));
master_name=calloc(100,sizeof(char));
logfilename=calloc(100,sizeof(char));
for(i=0;i<=MAX_SLAVES;i++) slaves[i]=calloc(100,sizeof(char));
if(parse_config()==0) {
printf("\nConfiguration file is bad or missing\n");
exit(0);
}
if(slave==2) {
printf("APC SmartUPS daemon started on port %s with timeout %d mins.\n",use_port,power_timer);
printf("APC SmartUPS daemon logging interval %d seconds.\n",log_timer);
for(i=0;i<num_slaves;i++) printf("Slave: %s\n",slaves[i]);
}
else printf("APC SmartUPS daemon started in slave mode. Master is %s\n",master_name);
killme = 0;
/* Initialize system log */
openlog("apcd", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_LOCAL2);
/* If we are master, we are responsible for UPS statistic log */
if(slave == 2) {
UPSlogfile=fopen(logfilename,"a");
}
/* Become daemon */
start_daemon();
signal_setup();
syslog(LOG_INFO,"Starting apcd version %s",VERSION);
if(slave==2) {
syslog(LOG_INFO,"Master mode port %s timeout %d",use_port,power_timer);
syslog(LOG_INFO,"UPS statistics in %s",logfilename);
for(i=0;i<num_slaves;i++) syslog(LOG_INFO,"Slave: %s",slaves[i]);
}
if(slave==1) syslog(LOG_INFO,"Slave mode. Master is %s",master_name);
if(slave==2) setup_tty();
battlow=0;
alarmup=0;
alarmdown=0;
pending=0;
wasmsg=0;
/* Open socket for network communication */
if(slave == 1) {
if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
syslog(LOG_ERR,"Can't open stream socket");
}
}
if(slave == 2) {
prepare_master();
}
if(slave == 1) prepare_slave(); /* If slave, do some network stuff */
if(slave == 1) {
while(!killme) {
if(get_master_message(newsocketfd) == 0) {
if(gottimeout) {
gottimeout = 0;
syslog(LOG_INFO,"Power failure, %d minutes to shutdown",mastertimeout);
if(mastertimeout == 0) go_down();
else {
sprintf(msg,"\n\nAPC Daemon: Power failure, system will go down in %d minutes.\n",mastertimeout);
mesall(msg);
wasmsg=1;
}
}
if(gotpowerok) {
gotpowerok = 0;
syslog(LOG_INFO,"Power restored, shutdown cancelled");
if(wasmsg) {
sprintf(msg,"\n\nAPC Daemon: Power restored, shutdown cancelled.\n");
mesall(msg);
wasmsg=0;
}
}
if(masterbatlow) go_down_batt();
}
}
}
else {
while(!killme) {
fillUPS(port,&myUPS);
sleep(1);
if (pending) {
alarmcount--;
if (alarmcount == 0) {
send_to_slaves(0);
go_down();
pending=0;
}
if(((alarmcount % 60) == 0) && (alarmcount != 0)) {
send_to_slaves(alarmcount);
sprintf(msg,"\n\nAPC Daemon: Power failure, system will go down in %d minutes.\n",alarmcount/60);
mesall(msg);
wasmsg=1;
}
}
if (alarmup) {
if(!pending) {
alarmcount=power_timer*60;
pending=1;
}
alarmup=0;
}
if (alarmdown) {
if(wasmsg) {
send_to_slaves(-1);
sprintf(msg,"\n\nAPC Daemon: Power restored, shutdown cancelled\n");
mesall(msg);
wasmsg=0;
}
alarmcount=0;
pending=0;
alarmdown=0;
}
if (battlow) go_down_batt();
}
}
if(killme == 1) {
syslog(LOG_INFO,"Ending apcd version %s",VERSION);
if(slave==2) {
tcsetattr(port,TCSANOW,&oldtio);
close(port);
}
if (slave==1) close(socketfd);
if (slave==2) {
for(i=0;i<num_slaves;i++) close(slavesocket[i]);
}
closelog();
if (slave == 1) fclose(UPSlogfile);
}
if(killme == 2) {
if(slave==2) send_to_slaves(-2);
mesall("APC Daemon: SYSTEM IS GOING DOWN NOW !!!");
if (slave==1) close(socketfd);
if (slave==2) {
for(i=0;i<num_slaves;i++) close(slavesocket[i]);
}
do_shutdown();
}
};
/* Setup of the communication port. Hope it will work
*/
void setup_tty()
{
port=open(use_port,O_RDWR | O_NOCTTY);
if (port < 0) {
syslog(LOG_ERR,"Unable to open port %s",use_port);
exit(-1);
}
tcgetattr(port,&oldtio); /* Save old settings */
newtio.c_cflag = DEFAULT_SPEED | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR; /* Ignore errors, raw input */
newtio.c_oflag = 0; /* Raw output */
newtio.c_lflag = 0; /* No local echo */
newtio.c_cc[VMIN] = 1;
newtio.c_cc[VTIME] = 0;
tcflush(port,TCIFLUSH);
tcsetattr(port,TCSANOW,&newtio);
}
/* Become a daemon, release stdin, stdout etc.
*/
void start_daemon()
{
int pid;
close(0);
close(1);
close(2);
if ((pid=fork()) < 0) {
syslog(LOG_ERR,"Unable to fork");
exit(1);
}
if (pid != 0) exit(0);
};
/* Setup various signal handlers. Code here is adapted from diald program
* which is (c) Eric Schenk.
*/
void signal_setup()
{
sigset_t sigmask;
struct sigaction sa;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGINT); /* Termination requested */
sigaddset(&sigmask, SIGTERM); /* Termination requested */
if(slave==2) sigaddset(&sigmask, SIGUSR1); /* Dump UPS stats */
#define SIGNAL(s,handler) { \
sa.sa_handler = handler; \
if (sigaction(s, &sa, NULL) < 0) { \
syslog(LOG_ERR, "sigaction(%d) failed ", s); \
exit(1); \
} \
}
sa.sa_mask = sigmask;
sa.sa_flags = 0;
SIGNAL(SIGINT,sig_intr);
SIGNAL(SIGTERM,sig_term);
if(slave==2) SIGNAL(SIGUSR1,dump_status);
}
void sig_intr(int sig)
{
syslog(LOG_INFO,"SIGINTR Termination requested");
killme = 1;
}
void sig_term(int sig)
{
syslog(LOG_INFO,"SIGTERM Termination requested");
killme=1;
}
void dump_status(int sig)
{
valfile=fopen("/tmp/upsstat","w");
fprintf(valfile,"ULINE:%.1f\n",myUPS.LineVoltage);
fprintf(valfile,"MLINE:%.1f\n",myUPS.LineMax);
fprintf(valfile,"NLINE:%.1f\n",myUPS.LineMin);
fprintf(valfile,"FLINE:%.1f\n",myUPS.LineFreq);
fprintf(valfile,"VOUTP:%.1f\n",myUPS.OutputVoltage);
fprintf(valfile,"LOUTP:%.1f\n",myUPS.UPSLoad);
fprintf(valfile,"BOUTP:%.1f\n",myUPS.BattVoltage);
fprintf(valfile,"BCHAR:%.1f\n",myUPS.BatLoad);
fprintf(valfile,"BFAIL:%d\n",battlow);
fprintf(valfile,"UTEMP:%.1f\n",myUPS.UPSTemp);
if(pending) {
fprintf(valfile,"UBATT:1\n");
fprintf(valfile,"UPOWR:0\n");
}
else {
fprintf(valfile,"UBATT:0\n");
fprintf(valfile,"UPOWR:1\n");
};
fprintf(valfile,"UTST1:NONE\n");
fprintf(valfile,"UTST2:NONE\n");
fprintf(valfile,"UTST3:NONE\n");
fprintf(valfile,"UTST4:NONE\n");
fprintf(valfile,"UTST5:NONE\n");
fprintf(valfile,"UTST6:NONE\n");
fprintf(valfile,"UTST7:NONE\n");
fprintf(valfile,"UTST8:NONE\n");
fprintf(valfile,"UTST9:NONE\n");
fprintf(valfile,"UTST0:NONE\n");
fprintf(valfile,"UUSR1:NONE\n");
fprintf(valfile,"UUSR2:NONE\n");
fprintf(valfile,"UUSR3:NONE\n");
fprintf(valfile,"UUSR4:NONE\n");
fprintf(valfile,"UUSR5:NONE\n");
fprintf(valfile,"UUSR6:NONE\n");
fprintf(valfile,"UUSR7:NONE\n");
fprintf(valfile,"UUSR8:NONE\n");
fprintf(valfile,"UUSR9:NONE\n");
fprintf(valfile,"UUSR0:NONE\n");
fclose(valfile);
}
void go_down()
{
syslog(LOG_INFO,"System is going down - power failure");
killme=2;
}
void go_down_batt()
{
syslog(LOG_INFO,"System is going down - battery low");
killme=2;
}
int getline(int fd, char *s)
{
int i,ending;
char c;
i=0;
ending=0;
while (!ending) {
read(fd,&c,1);
switch(c) {
case UPS_ON_BATT: syslog(LOG_INFO,"UPS is going on battery");
alarmup=1;
break;
case UPS_ON_LINE: syslog(LOG_INFO,"UPS is going on-line");
alarmdown=1;
break;
case BATT_LOW: battlow=1;
break;
case BATT_OK: battlow=0;
break;
case '\n': ending=1;
break;
default: s[i++]=c;
break;
}
}
s[i]='\0';
return(0);
}
int fillUPS (int fd,UPSINFO *ups)
{
char answer[MAXLINE];
char q;
q='Y';
write(fd,&q,1);
getline(fd,answer);
q=BATT_FULL;
write(fd,&q,1);
getline(fd,answer);
ups->BatLoad=atof(answer);
q=UPS_LINE_MIN;
write(fd,&q,1);
getline(fd,answer);
ups->LineMin=atof(answer);
q=UPS_LINE_MAX;
write(fd,&q,1);
getline(fd,answer);
ups->LineMax=atof(answer);
q=UPS_LOAD;
write(fd,&q,1);
getline(fd,answer);
ups->UPSLoad=atof(answer);
q=LINE_FREQ;
write(fd,&q,1);
getline(fd,answer);
ups->LineFreq=atof(answer);
q=LINE_VOLTAGE;
write(fd,&q,1);
getline(fd,answer);
ups->LineVoltage=atof(answer);
q=OUTPUT_VOLTAGE;
write(fd,&q,1);
getline(fd,answer);
ups->OutputVoltage=atof(answer);
q=UPS_TEMP;
write(fd,&q,1);
getline(fd,answer);
ups->UPSTemp=atof(answer);
q=BATT_VOLTAGE;
write(fd,&q,1);
getline(fd,answer);
ups->BattVoltage=atof(answer);
q=UPS_STATUS;
write(fd,&q,1);
getline(fd,answer);
ups->Status=atoi(answer);
log_counter++;
if(log_counter >= log_timer) {
log_counter=0;
log_UPS_status();
}
return(0);
}
/* mesusr() and mesall() function are actually parts of shutdown source
* I am using them for sending messages before shutdown
*/
void mesusr(char *mess,struct utmp *ut)
{
int fd;
char term[40] = {'/','d','e','v','/',0};
(void)strncat(term,ut->ut_line,sizeof(ut->ut_line));
if((fd=open(term, O_RDWR | O_NONBLOCK)) < 0)
return;
write(fd,mess,strlen(mess));
close(fd);
}
void mesall(char *mess)
{
struct utmp *ut;
utmpname(_PATH_UTMP);
setutent();
ut=getutent();
while((ut = getutent())) {
if(ut->ut_type == USER_PROCESS)
mesusr(mess,ut);
}
endutent();
}
/*
*
* From here, there are parts of the source from shutdown.c which
* is a part of linux-utils-2.1
*
*/
void write_wtmp(), unmount_disks(), unmount_disks_ourselves();
void
do_shutdown()
{
/* struct itimerval new,old; */
char a;
/* setpriority(PRIO_PROCESS, 0, PRIO_MIN); */
chdir("/");
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, SIG_IGN);
/* do syslog message... */
syslog(LOG_INFO, "System cleanup");
closelog();
if (slave == 2) fclose(UPSlogfile);
sleep(1);
kill(1, SIGTSTP); /* tell init not to spawn more getty's */
write_wtmp();
sync();
signal(SIGTERM, SIG_IGN);
setpgrp(); /* so the shell wont kill us in the fall */
/* a gentle kill of all other processes except init */
kill(-1, SIGTERM);
sleep(2);
/* now use brute force... */
kill(-1, SIGKILL);
/* turn off accounting */
acct(NULL);
sync();
sleep(2);
/* unmount disks... */
unmount_disks();
sync();
sleep(1);
a = 'S';
if(slave==2) write(port,&a,1);
sleep(9999); /* Wait for UPS switch off */
reboot(0xfee1dead,672274793,0x1234567); /* Just for sure :-) */
/* NOTREACHED */
exit(0); /* to quiet gcc */
}
void
write_wtmp()
{
/* write in wtmp that we are dying */
int fd;
struct utmp ut;
memset((char *)&ut, 0, sizeof(ut));
strcpy(ut.ut_line, "~");
memcpy(ut.ut_name, "shutdown", sizeof(ut.ut_name));
time(&ut.ut_time);
ut.ut_type = BOOT_TIME;
if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) > 0) {
write(fd, (char *)&ut, sizeof(ut));
close(fd);
}
}
void
unmount_disks()
{
/* better to use umount directly because it may be smarter than us */
int pid;
int result;
int status;
sync();
if ((pid = fork()) < 0) {
printf("Cannot fork for umount, trying manually.\n");
unmount_disks_ourselves();
return;
}
if (!pid) {
execl(_PATH_UMOUNT, UMOUNT_ARGS, NULL);
printf("Cannot exec %s, trying umount.\n", _PATH_UMOUNT);
execlp("umount", UMOUNT_ARGS, NULL);
printf("Cannot exec umount, trying manually.\n");
unmount_disks_ourselves();
exit(0);
}
while ((result = wait(&status)) != -1 && result != pid)
;
if (result == -1 || status) {
printf("Running umount failed, trying manually.\n");
unmount_disks_ourselves();
}
}
void
unmount_disks_ourselves()
{
/* unmount all disks */
FILE *mtab;
struct mntent *mnt;
char *mntlist[128];
int i;
int n;
char *filesys;
sync();
if (!(mtab = setmntent(_PATH_MTAB, "r"))) {
printf("Cannot open %s.\n", _PATH_MTAB);
return;
}
n = 0;
while (n < 100 && (mnt = getmntent(mtab))) {
mntlist[n++] = strdup(mnt->mnt_fsname[0] == '/' ?
mnt->mnt_fsname : mnt->mnt_dir);
}
endmntent(mtab);
/* we are careful to do this in reverse order of the mtab file */
for (i = n - 1; i >= 0; i--) {
filesys = mntlist[i];
#ifdef DEBUGGING
printf("umount %s\n", filesys);
#else
if (umount(mntlist[i]) < 0)
printf("Couldn't umount %s\n", filesys);
#endif
}
}
void log_UPS_status()
{
char msg[100];
time_t nowtime;
time(&nowtime);
strftime(msg,100,"%b %d %X",localtime(&nowtime));
fprintf(UPSlogfile,"%s APC: ",msg);
fprintf(UPSlogfile,"%.1f %.1f %.1f ",myUPS.BatLoad,myUPS.LineMin,myUPS.LineMax);
fprintf(UPSlogfile,"%.1f %.1f %.1f ",myUPS.UPSLoad,myUPS.LineFreq,myUPS.LineVoltage);
fprintf(UPSlogfile,"%.1f %.1f %.1f\n",myUPS.OutputVoltage,myUPS.UPSTemp,myUPS.BattVoltage);
fflush(UPSlogfile);
}